package com.fhsk.file.manager.shiro.realm;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fhsk.file.business.common.JwtUtil;
import com.fhsk.file.business.entity.dto.user.UserDto;
import com.fhsk.file.business.entity.vo.user.UserVo;
import com.fhsk.file.business.service.IUserService;
import com.fhsk.file.common.enums.RedisKeyEnum;
import com.fhsk.file.common.enums.StatusEnum;
import com.fhsk.file.common.exception.AuthenticationException;
import com.fhsk.file.common.utils.RedisComponent;
import com.fhsk.file.manager.shiro.token.JwtToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashSet;
import java.util.Set;

/**
 * 自定义shiro realm
 *
 * @author: panyi
 * @date: 2020-1-21
 */
@Component
@Slf4j
public class MyRealm extends AuthorizingRealm {

    @Resource
    private IUserService userService;

//    @Resource
//    private IRoleService roleService;

//    @Resource
//    private IRoleMenuService roleMenuService;
//
//    @Resource
//    @Lazy
//    private IUserCompanyService userCompanyService;

    @Resource
    private RedisComponent redisComponent;

    @Autowired
    HttpServletRequest request;


    private final static Integer NO_DATA = -1;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * 授权
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//        Set<String> permission = new HashSet<>();
        Set<String> roleName = new HashSet<>();
        //获取登录用户信息
        UserVo user = userService.getCurrentUser();
        if (null == user) {
            throw new UnknownAccountException();
        }
        //获取当前登录的单个角色id
//        Integer roleId = user.getRoleId();

//        if (roleId != null) {
        //获取用户角色信息
//            RoleDto role = roleService.getRoleByIdCache(roleId);
//            roleName.add(role.getRoleName());
        //获取用户的权限信息
//            List<Menu> permissionList = roleMenuService.getPermissionByRoleIdCache(role.getId());
//            for (Menu menu : permissionList) {
//                permission.add(menu.getPermission());
//            }
//        }

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //根据用户名去数据库查询授权角色
        simpleAuthorizationInfo.setRoles(roleName);
        //根据用户名去数据库查询授权权限
//        simpleAuthorizationInfo.setStringPermissions(permission);
        return simpleAuthorizationInfo;

    }

    /**
     * 认证
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
        String token = (String) authenticationToken.getCredentials();// 校验token有效性
        String username = JwtUtil.getUserName(token);
        if (StrUtil.isBlank(token)) {
            throw new AuthenticationException(406, "您的登录已失效，请退出重新登录!");
        }
        // 查询用户信息
        UserDto userDto = userService.getOne(new LambdaQueryWrapper<UserDto>()
                .eq(UserDto::getAccount, username).eq(UserDto::getIsDelete, StatusEnum.NOT_DELETE.getCode()));
        if (userDto == null) {
            throw new AuthenticationException(500, "用户名或者密码错误,请检查是否正确!");
        }
        //其次验证token
        try {
            JwtUtil.verify(token, username, userDto.getPassword());
        } catch (RuntimeException e) {
            throw new AuthenticationException(406, "您的登录已失效，请退出重新登录！");
        }
        //验证token是否在缓存中
        if (!verifyToken(userDto, token)) {
            throw new AuthenticationException(406, "您的登录已失效，请退出重新登录！");
        }
        // 判断用户状态
        if (StatusEnum.DISABLE.getCode().equals(userDto.getIsLocked())) {
            log.warn("账号已被禁用,请联系管理员!");
            throw new AuthenticationException(406, "账号已被禁用,请联系管理员!");
        }

        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(userDto, userVo);
        //如果成功登陆则将用户信息存入request
        UserVo initUser = initUser(userVo);
        request.setAttribute("currentUser", initUser);
        return new SimpleAuthenticationInfo(userDto, token, ByteSource.Util.bytes(userDto.getAccount()), getName());
    }

    /**
     * 验证token是否在缓存内
     *
     * @param user  用户信息
     * @param token token
     * @return 是否存在
     * @description
     * @author Neal.Zhi
     * @date 2020/9/2
     */
    private Boolean verifyToken(UserDto user, String token) {
        //先通过缓存取值
        String tokenKey = RedisKeyEnum.TOKEN.getValue() + user.getAccount();
        return redisComponent.sHasKey(tokenKey, token);
    }

    private UserVo initUser(UserVo userVo) {
        //先通过缓存取值
        String userKey = RedisKeyEnum.USER.getValue() + userVo.getAccount();
        boolean hasKey = redisComponent.hasKey(userKey);
        UserVo cacheUser = null;
        if (hasKey) {
            try {
                Object objUser = redisComponent.get(userKey);
                cacheUser = JSON.parseObject(objUser.toString(), UserVo.class);
            } catch (RuntimeException e) {
                // do nothing
            }
        }
        if (cacheUser != null) {
            return cacheUser;
        }
        //如果缓存未命中,则进行查库操作
        //判断登录是否传值roleId,如果没有默认为id倒序第一个角色
//            Integer roleId = user.getRoleId();
//            if (roleId == null) {
//                List<RoleDto> roleListByUserId = roleService.getRoleListByUserId(user.getId());
//                if (roleListByUserId != null && roleListByUserId.size() > 0) {
//                    RoleDto role = roleListByUserId.get(0);
//                    user.setRoleId(role.getId());
//                    user.setRoleName(role.getRoleName());
//                    user.setRoleDescription(role.getDescription());
//                }
//            } else {
//                RoleDto byId = roleService.getById(roleId);
//                String roleName = Optional.ofNullable(byId).map(RoleDto::getRoleName).orElse("anonymous");
//                Integer getRoleId = Optional.ofNullable(byId).map(RoleDto::getId).orElse(-1);
//                user.setRoleId(getRoleId);
//                user.setRoleName(roleName);
//            }

        //查询companyId,进行数据权限公司id赋值(当前员工只能看到当前公司的id)
//            CompanyBase company = userCompanyService.getCompanyByUserid(user.getId());
//            Integer companyId = Optional.ofNullable(company).map(CompanyBase::getId).orElse(NO_DATA);
//            //查询parentId,判断是否是二三级单位
//            String isFirstLevel = Optional.ofNullable(company).map(CompanyBase::getIsFirstLevel).orElse("0");
//
//            boolean isTop = "1".equals(isFirstLevel);
//
//            user.setCompanyId(companyId);
//            user.setTop(isTop);

        //放入缓存
        redisComponent.set(userKey, userVo);

        return userVo;
    }
}
